// Include basic c stuff
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#include <termios.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <unistd.h>
#include <errno.h>

// Include "directory" listing (/dev) stuff
#include <dirent.h>

// Include regularar expression stuff
#include <regex.h>

// Include local JNI interface stub
#include "SerialInterface.h"

// Local defines
#define BUFFERLENGTH 4096
#define MAX_ERR_LENGTH 80

// C data prototypes

// SerialInterface class field information
typedef struct serial_interface_fields {
  // Create SerialInterface class reference
  jclass jnicSerialInterface;

  // Create device name field id for this class
  jfieldID jnifidDeviceName;

  // Create device name field id for this class
  jfieldID jnifidDeviceFd;

  // Create device speed field id for this class
  jfieldID jnifidDeviceSpeed;

  // Create device parity field id for this class
  jfieldID jnifidDeviceParity;

  // Create device databits field for this class
  jfieldID jnifidDeviceDatabits;

  // Create device stopbits field for this class
  jfieldID jnifidDeviceStopbits;

  // Create device name field id for this class
  jfieldID jnifidDeviceAvailable;

  // Create device name field id for this class
  jfieldID jnifidDeviceOpen;
};

// C function prototypes...
int device_filter(const struct dirent*);

int initialize_proxy(JNIEnv *, jobject, char*);
int update_proxy(JNIEnv *, jobject);
int update_hardware(JNIEnv *, jobject);

void print_termios(struct termios*);
speed_t convert_jni_speed(jint);
jint convert_termios_speed(speed_t);
tcflag_t convert_jni_parity(jint);
jint convert_termios_parity(tcflag_t);
tcflag_t convert_jni_databit(jint);
jint convert_termios_databit(tcflag_t);
tcflag_t convert_jni_stopbit(jint);
jint convert_termios_stopbit(tcflag_t);

// XXX Device directory
char* device_dir = "/dev";

// XXX Use regexp to match...
// Yes, this is an awful optimization to prevent recompilation
// for each call to the filter function...
char* regexp = "ttyS+";
int regexp_err;
regex_t myre;
char err_msg[MAX_ERR_LENGTH];

// XXX Define struct with proxy class field references
// Yes, this is an awful design to make parameter passing
// simpler...
struct serial_interface_fields sif;

/*-----------------------------------------------------------------*/
/* JNI interface implmentation                                     */
/*-----------------------------------------------------------------*/

/*
 * Class:     se_ytterman_jserial_jni_SerialInterfaceFactory
 * Method:    getInterfaceArray
 * Signature: ()[Lse/ytterman/jserial/jni/SerialInterface;
 */
JNIEXPORT jobjectArray JNICALL Java_se_ytterman_jserial_jni_SerialInterfaceFactory_getInterfaceArray
  (JNIEnv * env, jobject jnioSerialInterfaceFactory)
{
  // List the devices in /dev that matches "ttyS*"
  int ifc_index, num_devices;
  struct dirent **device_list;

  // XXX Yeuch! The regexp shouldnt be hard-coded
  regexp_err = regcomp(&myre, regexp, REG_EXTENDED);

  if (regexp_err != 0) {
    // XXX regexp compilation error
    // Throw exception...
    regerror(regexp_err, &myre, err_msg, MAX_ERR_LENGTH);
    fprintf(stderr, "Error analyzing regular expression '%s': %s.\n", regexp, err_msg);
  }
  
  // XXX Yuack! The path shouldn't be hard-coded
  num_devices = scandir(device_dir, &device_list, device_filter, alphasort); 
  
  if(num_devices < 0) {
    // XXX throw exception
  }

  // DEBUG
  // fprintf(stderr, "Number of entries is %d\n", num_devices);

  // Create SerialInterface class reference
  jclass jnicSerialInterface = env->FindClass("se/ytterman/jserial/jni/SerialInterface");

  // Create device name field id for this class
  sif.jnifidDeviceName = env->GetFieldID(jnicSerialInterface, "device_name", "Ljava/lang/String;");

  // Create device name field id for this class
  sif.jnifidDeviceFd = env->GetFieldID(jnicSerialInterface, "device_fd", "I");

  // Create device speed field id for this class
  sif.jnifidDeviceSpeed = env->GetFieldID(jnicSerialInterface, "device_speed", "I");

  // Create device parity field id for this class
  sif.jnifidDeviceParity = env->GetFieldID(jnicSerialInterface, "device_parity", "I");

  // Create device databits field for this class
  sif.jnifidDeviceDatabits = env->GetFieldID(jnicSerialInterface, "device_databits", "I");

  // Create device stopbits field for this class
  sif.jnifidDeviceStopbits = env->GetFieldID(jnicSerialInterface, "device_stopbits", "I");

  // Create device name field id for this class
  sif.jnifidDeviceAvailable = env->GetFieldID(jnicSerialInterface, "available", "Z");

  // Create device name field id for this class
  sif.jnifidDeviceOpen = env->GetFieldID(jnicSerialInterface, "open", "Z");

  // Create an array of SerialInterface objects
  jobjectArray objIfcArray = static_cast<jobjectArray>(env->NewObjectArray(num_devices, jnicSerialInterface, NULL));

  // DEBUG
  // fprintf(stderr, "Iterate over all entries...\n");

  // Iterate...
  for (ifc_index = 0; ifc_index < num_devices; ifc_index++) {
    // Create full device path...
    char device_path[PATH_MAX];
    snprintf(device_path, PATH_MAX, "%s/%s", device_dir, device_list[ifc_index]->d_name);

    // Fetch method ID for default constructor of SerialInterface
    jmethodID jnimidSerialInterfaceDefault = env->GetMethodID(jnicSerialInterface, "<init>", "()V");

    // Create object instance of SerialInterface
    jobject jnioSerialInterface = env->NewObject(jnicSerialInterface, jnimidSerialInterfaceDefault);

    // DEBUG Open serial port, close is done by SerialInterface finalizer(?)...
    // fprintf(stderr, "Attempting to open %s...\n", device_path);

    // Initialize proxy object...
    initialize_proxy(env, jnioSerialInterface, device_path);

    // Finally, add PcapInterface object to array
    env->SetObjectArrayElement(objIfcArray, ifc_index, jnioSerialInterface);
  }

  // Free compiled regular expression
  regfree(&myre);

  // Free list of devices
  free(device_list);

  // return reference
  return objIfcArray;
}

/*
 * Class:     se_ytterman_jserial_jni_SerialInterface
 * Method:    setSpeed
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_se_ytterman_jserial_jni_SerialInterface_setSpeed
  (JNIEnv * env, jobject jnioSerialInterface, jint jni_speed) 
{
  // Set value in proxy object
  env->SetIntField(jnioSerialInterface, 
		   sif.jnifidDeviceSpeed, 
		   jni_speed);
  
  // Update hardware
  update_hardware(env, jnioSerialInterface);
}

/*
 * Class:     se_ytterman_jserial_jni_SerialInterface
 * Method:    setParity
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_se_ytterman_jserial_jni_SerialInterface_setParity
  (JNIEnv * env, jobject jnioSerialInterface, jint jni_parity)
{
  // Set value in proxy object
  env->SetIntField(jnioSerialInterface, 
		   sif.jnifidDeviceParity, 
		   jni_parity);
  
  // Update hardware
  update_hardware(env, jnioSerialInterface);
}

/*
 * Class:     se_ytterman_jserial_jni_SerialInterface
 * Method:    setDatabits
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_se_ytterman_jserial_jni_SerialInterface_setDatabits
  (JNIEnv * env, jobject jnioSerialInterface, jint jni_databits)
{
  // Set value in proxy object
  env->SetIntField(jnioSerialInterface, 
		   sif.jnifidDeviceDatabits,
		   jni_databits);
  
  // Update hardware
  update_hardware(env, jnioSerialInterface);
}

/*
 * Class:     se_ytterman_jserial_jni_SerialInterface
 * Method:    setStopbits
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_se_ytterman_jserial_jni_SerialInterface_setStopbits
  (JNIEnv * env, jobject jnioSerialInterface, jint jni_stopbits)
{
  // Set value in proxy object
  env->SetIntField(jnioSerialInterface, 
		   sif.jnifidDeviceStopbits,
		   jni_stopbits);
  
  // Update hardware
  update_hardware(env, jnioSerialInterface);
}

/*
 * Class:     se_ytterman_jserial_jni_SerialInterface
 * Method:    read
 * Signature: ([B)V
 */
JNIEXPORT jbyteArray JNICALL Java_se_ytterman_jserial_jni_SerialInterface_read
  (JNIEnv * env, jobject jnioSerialInterface)
{
  // Return value
  jbyteArray jniarrBuffer = NULL;
 
  // Fetch device fd from object
  int device_fd = (int)env->GetIntField(jnioSerialInterface, 
					sif.jnifidDeviceFd);  

  // Create byte buffer to work with
  u_char buffer[BUFFERLENGTH];  
  bzero(buffer, BUFFERLENGTH);
  
  // Read bytes from device...
  int read_bytes = read(device_fd, buffer, BUFFERLENGTH);

  // Check result...
  if(read_bytes > 0) {
    // Create new byte array java object
    jniarrBuffer = env->NewByteArray(read_bytes);
    
    // Copy the contents into the array
    jbyte *jnibBuffer = env->GetByteArrayElements(jniarrBuffer, 0);
    memcpy(jnibBuffer, buffer, (jsize)read_bytes);
    env->ReleaseByteArrayElements(jniarrBuffer, jnibBuffer, 0);
  }
  else if(read_bytes == -1 && errno == EAGAIN) {   
    // XXX(?) Or empty array when out of data...
    jniarrBuffer = env->NewByteArray(0);
  }
  else {
    perror("read error");
    // XXX Throw exception
  }

  return jniarrBuffer;
}

/*
 * Class:     se_ytterman_jserial_jni_SerialInterface
 * Method:    write
 * Signature: ([B)V
 */
JNIEXPORT void JNICALL Java_se_ytterman_jserial_jni_SerialInterface_write
  (JNIEnv * env, jobject jnioSerialInterface, jbyteArray jniarrBuffer)
{
  // Fetch device fd from object
  int device_fd = (int)env->GetIntField(jnioSerialInterface, 
					sif.jnifidDeviceFd);  

  // Create byte buffer to work with
  u_char buffer[BUFFERLENGTH];

  // Fetch length and pointer to bytes in array
  jsize length = env->GetArrayLength(jniarrBuffer);;
  jbyte * jnibBuffer = env->GetByteArrayElements(jniarrBuffer, 0);
  memcpy(buffer, jnibBuffer, length);
  env->ReleaseByteArrayElements(jniarrBuffer, jnibBuffer, 0);
  
  int written_bytes = write(device_fd, buffer, length);

  if(written_bytes < 0) {
    perror("write error");

    // XXX Throw exception?
  }
}

/*
 * Class:     se_ytterman_jserial_jni_SerialInterface
 * Method:    close
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_se_ytterman_jserial_jni_SerialInterface_close
  (JNIEnv *env , jobject jnioSerialInterface)
{
  // XXX Should this ever happen?
}

/*-----------------------------------------------------------------*/
/* Helper function implmentation                                   */
/*-----------------------------------------------------------------*/

// Pattern based device file matching...
int device_filter(const struct dirent* device) 
{
  int result = 0;

  // And filter...
  regexp_err = regexec(&myre, device->d_name, 0, NULL, 0);

  switch(regexp_err) {
  case 0:
    result = 1;
    break;

  case REG_NOMATCH:
    result = 0;
    break;
    
  default:
    // XXX rexexp handling error
    // This should be taken care of...
    regerror(regexp_err, &myre, err_msg, MAX_ERR_LENGTH);
    fprintf(stderr, "Error using regular expression: %s.\n", regexp, err_msg);
    break; 
  }

  return result;
}  


int initialize_proxy(JNIEnv * env, jobject jnioSerialInterface, char* device_path) {
    // Create device name 
    jstring jniStrDeviceName = env->NewStringUTF(device_path);
    
    // Link String object reference into SerialInterface object
    env->SetObjectField(jnioSerialInterface, 
			sif.jnifidDeviceName, 
			jniStrDeviceName);
    
    // Try to open device
    // int serial_fd = open(device_path, O_RDWR | O_NOCTTY | O_NONBLOCK );
    int serial_fd = open(device_path, O_RDWR | O_NOCTTY);

    if(serial_fd < 0) {
      // DEBUG
      fprintf(stderr, "Failed to open %s...\n", device_path);

      // Set the open state value in the SerialInterface instance to FALSE
      env->SetBooleanField(jnioSerialInterface, 
			   sif.jnifidDeviceOpen, 
			   JNI_FALSE);
      
      // XXX Something went wrong, throw exception
      perror(device_path);
    }
    else {
      // DEBUG
      // fprintf(stderr, "Successfully opened %s...\n", device_path);

      // Set the device file descriptor value in the SerialInterface instance
      env->SetIntField(jnioSerialInterface, 
		       sif.jnifidDeviceFd, 
		       (jint)serial_fd);

      // Set the open state value in the SerialInterface instance to TRUE
      env->SetBooleanField(jnioSerialInterface, 
			   sif.jnifidDeviceOpen, 
			   JNI_TRUE);
    }

    // Update hardware state from proxy object
    update_hardware(env, jnioSerialInterface);

    // Update proxy object state from hardware
    update_proxy(env, jnioSerialInterface);
}

// Update proxy object state from hardware
int update_proxy(JNIEnv * env, jobject jnioSerialInterface) {
  // Termios storage
  struct termios tio;

  // Fetch device fd from object
  int device_fd = (int)env->GetIntField(jnioSerialInterface, 
					sif.jnifidDeviceFd);

  // Return value is read success
  int read_result = tcgetattr(device_fd, &tio);

  if(read_result < 0) {
    // Set the SerialInterface state to available=false, read of interface
    // settings not possible, may be missing hardware and/or permissions
    env->SetBooleanField(jnioSerialInterface, 
			 sif.jnifidDeviceAvailable, 
			 JNI_FALSE);

    // XXX Throw exception(?)
    return -1;
  }
  else {
    // Set the SerialInterface state to available=true
    env->SetBooleanField(jnioSerialInterface, 
			 sif.jnifidDeviceAvailable, 
			 JNI_TRUE);
  }    

  // Read speed...
  speed_t input_speed = cfgetispeed(&tio);
  speed_t output_speed = cfgetospeed(&tio);

  if(input_speed == output_speed) {
    // Set value in proxy object
    env->SetIntField(jnioSerialInterface, 
		     sif.jnifidDeviceSpeed, 
		     convert_termios_speed(input_speed));
  }
  else {
    // XXX We dont support asymetric speeds at this stage...
  }
    
  // Convert and set parity value in proxy object...
  env->SetIntField(jnioSerialInterface,
		   sif.jnifidDeviceParity,
		   convert_termios_parity(tio.c_cflag));
	
  // Convert and set databit value in proxy object...
    env->SetIntField(jnioSerialInterface,
		     sif.jnifidDeviceDatabits,
		     convert_termios_databit(tio.c_cflag));
    
    // Convert and set stopbit value in proxy object...
    env->SetIntField(jnioSerialInterface,
		     sif.jnifidDeviceStopbits,
		     convert_termios_stopbit(tio.c_cflag));
    
  return 0;
}

// Update hardware state from proxy object
int update_hardware(JNIEnv * env, jobject jnioSerialInterface) {
  // Termios storage
  struct termios tio;

  // Fetch device fd from object
  int device_fd = (int)env->GetIntField(jnioSerialInterface, 
					sif.jnifidDeviceFd);
  
  // Fetch proxy object values
  jint jni_speed = (int)env->GetIntField(jnioSerialInterface,
					 sif.jnifidDeviceSpeed);

  jint jni_databits = (int)env->GetIntField(jnioSerialInterface,
					 sif.jnifidDeviceDatabits);

  jint jni_parity = (int)env->GetIntField(jnioSerialInterface,
					  sif.jnifidDeviceParity);

  jint jni_stopbits = (int)env->GetIntField(jnioSerialInterface,
					    sif.jnifidDeviceStopbits);

  // Convert...
  speed_t  speed = convert_jni_speed(jni_speed);
  tcflag_t databits = convert_jni_databit(jni_databits);
  tcflag_t parity = convert_jni_parity(jni_parity);
  tcflag_t stopbits = convert_jni_stopbit(jni_stopbits);

  // Read and write...
  int read_result, write_result;

  read_result = tcgetattr(device_fd, &tio);
  
  // Ignore bytes with parity errors and break
  tio.c_iflag       = IGNPAR | IGNBRK;

  // Input without modifications 
  tio.c_oflag       = 0;
  
  // Set databits, parity and stopbints
  tio.c_cflag = (databits | parity | stopbits);

  // CRTSCTS : output hardware flow control
  // CLOCAL  : local connection, no modem control
  // CREAD   : enable receiving characters
  tio.c_cflag |= (CRTSCTS | CLOCAL | CREAD);
  // tio.c_cflag |= (CLOCAL | CREAD);

  // Set speed
  cfsetispeed(&tio, speed);
  cfsetospeed(&tio, speed);

  // No "canonical" mode or character conversions
  tio.c_lflag       = 0;

  // Return whan at least one character has been read
  tio.c_cc[VTIME]   = 0;
  tio.c_cc[VMIN]    = 1;
  
  write_result = tcsetattr(device_fd, TCSANOW, &tio);  

  return write_result;
}

// Debug print termios function
void print_termios(struct termios* tio) {
  // Debug printout should go into a function...
  fprintf(stderr, "Input mode flags    (c_iflag) %0.8X\n", tio->c_iflag);
  fprintf(stderr, "Output mode flags   (c_oflag) %0.8X\n" ,tio->c_oflag);
  fprintf(stderr, "Control mode flags  (c_cflag) %0.8X\n" ,tio->c_cflag);
  fprintf(stderr, "Local mode flags    (c_lflag) %0.8X\n", tio->c_lflag);
  // XXX cc_t c_line missing
  // XXX cc_t c_cc[] missing
  fprintf(stderr, "Input speed        (c_ispeed) %0.8X\n", tio->c_ispeed);
  fprintf(stderr, "Output speed       (c_ospeed) %0.8X\n", tio->c_ospeed);
}

// Convert between Java baud rates and c termios enum's
speed_t convert_jni_speed(jint jni_speed) {
  // Return value
  speed_t termios_speed = 0;

  switch(jni_speed) {
  case  se_ytterman_jserial_jni_SerialInterface_BAUD_U:
    termios_speed = B0;
    break;
  case se_ytterman_jserial_jni_SerialInterface_BAUD_50:
    termios_speed = B50;
    break;
  case se_ytterman_jserial_jni_SerialInterface_BAUD_75:
    termios_speed = B75;
    break;
  case se_ytterman_jserial_jni_SerialInterface_BAUD_110:
    termios_speed = B110;
    break;
  case se_ytterman_jserial_jni_SerialInterface_BAUD_134:
    termios_speed = B134;
    break;
  case se_ytterman_jserial_jni_SerialInterface_BAUD_150:
    termios_speed = B150;
    break;
  case se_ytterman_jserial_jni_SerialInterface_BAUD_200:
    termios_speed = B200;
    break;
  case se_ytterman_jserial_jni_SerialInterface_BAUD_300:
    termios_speed = B300;
    break;
  case se_ytterman_jserial_jni_SerialInterface_BAUD_600:
    termios_speed = B600;
    break;
  case se_ytterman_jserial_jni_SerialInterface_BAUD_1200:
    termios_speed = B1200;
    break;
  case se_ytterman_jserial_jni_SerialInterface_BAUD_1800:
    termios_speed = B1800;
    break;
  case se_ytterman_jserial_jni_SerialInterface_BAUD_2400:
    termios_speed = B2400;
    break;
  case se_ytterman_jserial_jni_SerialInterface_BAUD_4800:
    termios_speed = B4800;
    break;
  case se_ytterman_jserial_jni_SerialInterface_BAUD_9600:
    termios_speed = B9600;
    break;
  case se_ytterman_jserial_jni_SerialInterface_BAUD_19200:
    termios_speed = B19200;
    break;
  case se_ytterman_jserial_jni_SerialInterface_BAUD_38400:
    termios_speed = B38400;
    break;
  case se_ytterman_jserial_jni_SerialInterface_BAUD_57600:
    termios_speed = B57600;
    break;
  case se_ytterman_jserial_jni_SerialInterface_BAUD_115200:
    termios_speed = B115200;
    break;
  case se_ytterman_jserial_jni_SerialInterface_BAUD_230400:
    termios_speed = B230400;
    break;
  default:
    termios_speed = se_ytterman_jserial_jni_SerialInterface_BAUD_U;
  }
  
  return termios_speed;
}

// Convert between c termios enum's and Java baud rates
jint convert_termios_speed(speed_t termios_speed) {
  // Return value
  jint jni_speed = se_ytterman_jserial_jni_SerialInterface_BAUD_U;

  switch(termios_speed) {
  case B0:
    jni_speed = se_ytterman_jserial_jni_SerialInterface_BAUD_U;
    break;
  case B50:
    jni_speed = se_ytterman_jserial_jni_SerialInterface_BAUD_50;
    break;
  case B75:
    jni_speed = se_ytterman_jserial_jni_SerialInterface_BAUD_75;
    break;
  case B110:
    jni_speed = se_ytterman_jserial_jni_SerialInterface_BAUD_110;
    break;
  case B134:
    jni_speed = se_ytterman_jserial_jni_SerialInterface_BAUD_134;
    break;
  case B150:
    jni_speed = se_ytterman_jserial_jni_SerialInterface_BAUD_150;
    break;
  case B200:
    jni_speed = se_ytterman_jserial_jni_SerialInterface_BAUD_200;
    break;
  case B300:
    jni_speed = se_ytterman_jserial_jni_SerialInterface_BAUD_300;
    break;
  case B600:
    jni_speed = se_ytterman_jserial_jni_SerialInterface_BAUD_600;
    break;
  case B1200:
    jni_speed = se_ytterman_jserial_jni_SerialInterface_BAUD_1200;
    break;
  case B1800:
    jni_speed = se_ytterman_jserial_jni_SerialInterface_BAUD_1800;
    break;
  case B2400:
    jni_speed = se_ytterman_jserial_jni_SerialInterface_BAUD_2400;
    break;
  case B4800:
    jni_speed = se_ytterman_jserial_jni_SerialInterface_BAUD_4800;
    break;
  case B9600:
    jni_speed = se_ytterman_jserial_jni_SerialInterface_BAUD_9600;
    break;
  case B19200:
    jni_speed = se_ytterman_jserial_jni_SerialInterface_BAUD_19200;
    break;
  case B38400:
    jni_speed = se_ytterman_jserial_jni_SerialInterface_BAUD_38400;
    break;
  case B57600:
    jni_speed = se_ytterman_jserial_jni_SerialInterface_BAUD_57600;
    break;
  case B115200:
    jni_speed = se_ytterman_jserial_jni_SerialInterface_BAUD_115200;
    break;
  case B230400:
    jni_speed = se_ytterman_jserial_jni_SerialInterface_BAUD_230400;
    break;
  default:
    jni_speed = se_ytterman_jserial_jni_SerialInterface_BAUD_U;
  }
  
  return jni_speed;
}

// Convert between Java parity values and termios c_cflag bits
tcflag_t convert_jni_parity(jint jni_parity) {
  // Return value
  tcflag_t termios_parity = 0;
  
  switch (jni_parity) {
  case se_ytterman_jserial_jni_SerialInterface_PARITY_NO:  
    // PARENB bits = 0, PARODD bits = 0
    break;
  case se_ytterman_jserial_jni_SerialInterface_PARITY_ODD:
    termios_parity = PARENB | PARODD;
    break;
  case se_ytterman_jserial_jni_SerialInterface_PARITY_EVEN:                        
    termios_parity = PARENB;
    break;
  case se_ytterman_jserial_jni_SerialInterface_PARITY_MARK:
    // http://www.lothosoft.ch/thomas/libmip/markspaceparity.php
    break;
  case se_ytterman_jserial_jni_SerialInterface_PARITY_SPACE:
    // http://www.lothosoft.ch/thomas/libmip/markspaceparity.php
    break;
  }

  return termios_parity;
}

// Convert between termios c_cflag bits and Java parity values
jint convert_termios_parity(tcflag_t c_cflag) {
  // Return value
  jint jni_parity = se_ytterman_jserial_jni_SerialInterface_PARITY_U;
  
  if((c_cflag & (PARENB | PARODD)) == 0) {
    // PARENB bits = 0, PARODD bits = 0
    jni_parity = se_ytterman_jserial_jni_SerialInterface_PARITY_NO;
  }
  else if((c_cflag & (PARENB | PARODD)) == (PARENB | PARODD)) {
    // PARENB bits = 1, PARODD bits = 1
    jni_parity = se_ytterman_jserial_jni_SerialInterface_PARITY_ODD;
  }
  else if((c_cflag & (PARENB | PARODD)) == PARENB) {
    // PARENB bits = 1, PARODD bits = 0
    jni_parity = se_ytterman_jserial_jni_SerialInterface_PARITY_EVEN;
  }  

  return jni_parity;
}

// Convert between Java databit values and termios c_cflag bits
tcflag_t convert_jni_databit(jint jni_databit) {
  // Return value
  tcflag_t termios_databits = 0;

  switch(jni_databit) {
  case se_ytterman_jserial_jni_SerialInterface_DATA_8BITS:
    termios_databits = CS8;
    break;
  case se_ytterman_jserial_jni_SerialInterface_DATA_7BITS:
    termios_databits = CS7;
    break;
  case se_ytterman_jserial_jni_SerialInterface_DATA_6BITS:
    termios_databits = CS6;
    break;
  case se_ytterman_jserial_jni_SerialInterface_DATA_5BITS:
    termios_databits = CS5;
    break;
  }

  return termios_databits;
}

// Convert between termios c_cflag bits and Java databit values
jint convert_termios_databit(tcflag_t c_cflag) {
  // Return value
  jint jni_databit = se_ytterman_jserial_jni_SerialInterface_DATA_UBITS;

  if((c_cflag & CS8) == CS8) {
    jni_databit = se_ytterman_jserial_jni_SerialInterface_DATA_8BITS;
  }
  else if((c_cflag & CS7) == CS7) {
    jni_databit = se_ytterman_jserial_jni_SerialInterface_DATA_7BITS;
  }
  else if((c_cflag & CS6) == CS6) {
    jni_databit = se_ytterman_jserial_jni_SerialInterface_DATA_6BITS;
  }
  else if((c_cflag & CS5) == CS5) {
    jni_databit = se_ytterman_jserial_jni_SerialInterface_DATA_5BITS;
  }

  return jni_databit;
}

// Convert between Java stopbit values and c termios enum's
tcflag_t convert_jni_stopbit(jint jni_stopbit) {
  // Return value
  tcflag_t termios_stopbits = 0;

  switch (jni_stopbit) {
  case se_ytterman_jserial_jni_SerialInterface_STOP_1BITS:
    // STOPBITS = 0;
    break;
  case se_ytterman_jserial_jni_SerialInterface_STOP_2BITS:
    termios_stopbits = CSTOPB;
    break;
  }

  return termios_stopbits;
}

// Convert between c termios enum's and Java stopbit values
jint convert_termios_stopbit(tcflag_t c_cflag) {
  // Return value
  jint jni_stopbit = se_ytterman_jserial_jni_SerialInterface_STOP_UBITS;

  if((c_cflag & CSTOPB) == 0) {
    jni_stopbit = se_ytterman_jserial_jni_SerialInterface_STOP_1BITS;
  }
  else if((c_cflag & CSTOPB) == CSTOPB) {
    jni_stopbit = se_ytterman_jserial_jni_SerialInterface_STOP_2BITS;
  }

  return jni_stopbit;
}
